<?php
/**
 * Standard Model class for making query requests
 *
 * Model uses the PHP PDO Connection and imited to MySQL. Abstraction is for simple query requests.
 * Please use the query() method for making complex queries like those that use JOIN statements.
 */
class zapModel
{
	/**
	 * MySQL PDO Connection
	 *
	 * @var obj
	 */
    public $zPDO;

	/**
	 * ORDER BY arguement
	 *
	 * @var string
	 */
    public $zOrder;

	/**
	 * Database table in use
	 *
	 * @var string
	 */
    public $zTable;

	/**
	 * GROUP BY arguement
	 *
	 * @var string
	 */
    public $zGroup;

	/**
	 * LIMIT value
	 *
	 * @var int
	 */
    public $zLimit;

	/**
	 * OFFSET value
	 *
	 * @var int
	 */
	public $zOffset;

	/**
	 * INSERT columns and values
	 *
	 * @var array
	 */
    public $zFields;

	/**
	 * Database in use
	 *
	 * @var string
	 */
    public $zDatabase;

	/**
	 * Query arguements
	 *
	 * @var string
	 */
    public $zCondition;

	/**
	 * Request type (SELECT, INSERT, UPDATE, DELETE)
	 *
	 * @var string
	 */
    public $queryType;

    /**
     * Initialize database connection using MySQL PDO
     */
    public function __construct()
    {
        $file     = parse_ini_file(ROOT . 'application/settings/application.ini', true);
        $settings = $file['MySQL'];

        try {
            $this->zPDO = new PDO("mysql:host={$settings['hostname']};dbname={$settings['database']}",
                            	 $settings['username'],
                            	 $settings['password']);
        } catch (PDOException $e) {
            echo $e->getMessage();
            echo PDO::errorInfo();
        }
        
        $this->zDatabase = $settings['database'];
        $this->zTable   = str_replace('Model', '', get_class($this));
    }
    
    /**
     * Select specific or all fields
	 *
     * @param array $fields array of all fields to be queried
     * @return object
     */
    public function select($fields = array('*'))
    {
        if (is_array($fields)) {
            $this->zFields = $fields;
        } else {
            $this->zFields = array($fields);
        }

        $this->queryType = 'select';
        return $this;
    }

	/**
	 * Define query INSERT
	 *
	 * @param array $fields Columns and values to be inserted
	 * @return obj
	 */
    public function insert($fields = array())
    {
        $this->zFields   = $fields;
        $this->queryType = 'insert';
        return $this;
    }

	/**
	 * Define query UPDATE
	 *
	 * @param array $fields Column values to be updated
	 * @return obj
	 */
    public function update($fields = array())
    {
        $this->zFields   = $fields;
        $this->queryType = 'update';
        return $this;
    }

	/**
	 * Define query DELETE
	 *
	 * @param string $condition Delete arguements
	 * @return obj
	 */
    public function delete($condition)
    {
        $this->zCondition = $condition;
        $this->queryType = 'delete';
        return $this;
    }

	/**
	 * Define WHERE arguements
	 *
	 * @param string $condition WHERE arguements
	 */
    public function where($condition)
    {
        $this->zCondition = $condition;
        return $this;
    }

	/**
	 * Define GROUP BY arguements
	 *
	 * @param string $group Group by arguements
	 * @return obj
	 */
    public function group_by($group)
    {
        $this->zGroup = $group;
        return $this;
    }

	/**
	 * ORDER BY arguements
	 *
	 * @param string $order Order by arguements
	 * @return obj
	 */
    public function order_by($order)
    {
        $this->zOrder = $order;
        return $this;
    }

	/**
	 * Define LIMIT values
	 *
	 * @param int $offset Offset value
	 * @param int $limit Limit value
	 */
    public function limit($offset = 0, $limit)
    {
        $this->zOffset = $offset;
        $this->zLimit  = $limit;
        return $this;
    }

	/**
	 * Query builder method
	 *
	 * @return array
	 */
    public function execute()
    {
    	$values = array();

        switch ($this->queryType) {
        case 'insert':
            foreach ($this->zFields as $column => $value) {
                $columns[] = "`{$column}`";
                $values[]  = $value;
                $marker[]  = '?';
            }

            $columns = implode(', ', $columns);
            $marker  = implode(', ', $marker);
            $query   = "INSERT INTO `{$this->zTable}` ({$columns}) VALUES ({$marker})";
            break;
        case 'update':
            foreach ($this->zFields as $key => $value) {
                $setFields[] = "`{$key}` = ?";
                $values[]    = $value;
            }

            $setFields = join(', ', $setFields);
            $query     = "UPDATE `{$this->zTable}` SET {$setFields}";
            break;
        case 'delete':
            $query = "DELETE FROM `{$this->zTable}`";
            break;
        default:
            $fields = implode(', ', $this->zFields);
            $query  = "SELECT $fields FROM `{$this->zTable}`";
            break;
        }

        if ($this->zCondition) {
            $query .= " WHERE {$this->zCondition}";
        }
        
        if ($this->zGroup) {
            $query .= " GROUP BY {$this->zGroup}";
        }
        
        if ($this->zOrder) {
            $query .= " ORDER BY {$this->zOrder}";
        }
        
        if ($this->zLimit) {
            $query .= " LIMIT {$this->zOffset}, {$this->zLimit}";
        }
        
        $pdo = $this->zPDO->prepare($query);

        if ($this->zFields) {
            for ($i = 1; $i <= count($values); $i++)
                $pdo->bindParam($i, $values[$i - 1]);
        }

        $pdo->execute();

        $this->errorHandler($pdo->errorInfo(), $query);

		$result = array();
        if ($this->queryType == 'select') {
            while ($row = $pdo->fetch(PDO::FETCH_ASSOC)) {
                $result[] = $row;
            }
        } else {
            $result = $this->zPDO->lastInsertId();;
        }

		$this->resetVars();

		return $result;
    }

    /**
     * Shorthand method for selecting all records of a table
	 *
     * @return array
     */
    public function all()
    {
        return $this->select()->execute();
    }
	
    /**
     * Raw query requests
	 *
     * @param string $query
     * @return array
     */
    public function query($query)
    {
        $pdo = $this->zPDO->prepare($query);
        $pdo->execute();
        
        $this->resetVars();
        $this->errorHandler($pdo->errorInfo(), $query);
        
        $result = array();
        
        while ($row = $pdo->fetch(PDO::FETCH_ASSOC)) {
            $result[] = $row;
        }
        
        return $result;
    }

    /**
     * Reset variables for next query
     */
    private function resetVars()
    {
        $this->zOrder     = false;
        $this->zGroup     = false;
        $this->zLimit     = false;
        $this->zFields    = false;
        $this->zOffset    = false;
        $this->zCondition = false;
        $this->queryType = false;
    }

    /**
     * Display query errors
	 *
     * @param $errors array PDO error message
     * @param $query string MySQL query string
     */
    private function errorHandler($errors, $query)
    {
        if (isset($errors[1])) {
            $errorDetails = array(
                'query' => $query,
                'error' => $errors[2]
            );
            
            echo '<pre>';
            print_r($errorDetails);
            echo '</pre>';
            exit(1);
        }
    }
}